package beanstao.util.socket;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Map;

import beanstao.util.ByteUtils;
import beanstao.util.Encoding;
import beanstao.util.Strings;
import beanstao.util.log.ILog;
import beanstao.util.log.LogHelper;
import beanstao.util.socket.protocol.IProtocolFactory;
import beanstao.util.socket.protocol.IProtocolProcess;

/**
 * 描述服务器和客户端之间的交互。<br>
 * 解析协议,Responses客户端请求。<br>
 * 解析协议选择适当的IProtocol处理程序处理。
 * 
 * @author beanstao@google.com 
 */
public class SocketServerSession implements Runnable, ILog
{
	private final Map<Long, SocketServerSession> activeSessions;
	private final long sessionId;
	private final Socket socket;
	private final IProtocolFactory factory;
	private volatile boolean isClosed = false;
	private final boolean isDecodeHead;

	public SocketServerSession(final Map<Long, SocketServerSession> activeSessions, final Socket socket, final IProtocolFactory factory, final boolean isDecodeHead, final long id)
	{
		sessionId = id;

		this.activeSessions = activeSessions;
		this.socket = socket;
		this.factory = factory;
		this.isDecodeHead = isDecodeHead;
	}

	public Socket getSocket()
	{
		return socket;
	}

	/**
	 * @return 线程是否已经中断
	 */
	private boolean isInterrupted()
	{
		return Thread.currentThread().isInterrupted();
	}

	@Override
	public void run()
	{
		this.logDebug("Client " + socket.getRemoteSocketAddress() + " 连接成功。");
		activeSessions.put(sessionId, this);

		DataInputStream input = null;
		DataOutputStream output = null;

		BufferedInputStream binput = null;
		BufferedOutputStream boutput = null;

		String inString = null;

		try
		{
			// 2011-01-26 11:46:07
			// TODO：此处代码不理想，但是不会导致内存错误。下一步将修改，引入BufferManager。
			binput = new BufferedInputStream(socket.getInputStream(), 64000);
			boutput = new BufferedOutputStream(socket.getOutputStream(), 64000);
		}
		catch (Exception e)
		{
			this.logWarn("创建Bufferd流发生异常。" + e.getMessage(), e);
		}
		try
		{
			input = new DataInputStream(binput);
			output = new DataOutputStream(boutput);
		}
		catch (Exception e)
		{
			this.logWarn("创建DataInput流发生异常。" + e.getMessage(), e);
		}
		try
		{
			inString = this.parseInputStream(input, isDecodeHead);
		}
		catch (Exception e)
		{
			this.logWarn("将流转换为字符串时发生异常。", e);
		}
		if (!Strings.isEmpty(inString))
		{
			// 从工厂获得具体的处理类
			IProtocolProcess handler = factory.getProtocolHandler(inString);
			while (!this.isInterrupted() && !socket.isClosed() && !isClosed)
			{
				boolean isProcessComplate = false;
				try
				{// 转移到处理器去做协议的具体处理
					isProcessComplate = handler.process(inString, output);
				}
				catch (IOException e)
				{
					this.logWarn("数据处理时发生异常。" + e.getMessage(), e);
				}
				finally
				{
					try
					{
						output.flush();
					}
					catch (IOException e)
					{
					}
				}
				if (isProcessComplate)
					this.logDebug("客户端 " + socket.getRemoteSocketAddress() + " 信息处理完成。");
			}
			if (this.isInterrupted())
				this.logInfo(Thread.currentThread().getName() + " 被中断,关闭本次对话.");
		}
		try
		{
			if (binput != null)
				binput.close();
			if (boutput != null)
				boutput.close();
			if (input != null)
				input.close();
			if (output != null)
				output.close();
		}
		catch (IOException e)
		{
			this.logWarn("关闭流发生异常。" + e.getMessage(), e);
		}
		try
		{
			if (!socket.isClosed())
				socket.close();
		}
		catch (Exception e)
		{
			this.logError("关闭Socket连接时发生异常。", e);
		}
		finally
		{
			// 将自己从活动Session中移除
			activeSessions.remove(sessionId);
		}
	}

	/**
	 * 解析输入流为字符串
	 * 
	 * @param input
	 *            数据流
	 * @param isDecodeHead
	 *            是否解析头部
	 * @param object
	 *            调用本方法的主体对象，为写日志使用
	 * @return
	 * @throws Exception
	 */
	protected String parseInputStream(final InputStream input, final boolean isDecodeHead) throws Exception
	{
		DataInputStream reader = new DataInputStream(input);
		this.logDebug("已连接,开始InputStream解析......");
		int length = -1;
		try
		{
			if (isDecodeHead)
			{
				length = this.decodeHead(reader);
				this.logDebug("InputStream中数据长度:" + length);
			}
		}
		catch (Exception e)
		{
			throw e;
		}

		String msg = "";
		try
		{
			if (length > 0)
			{
				msg = this.decodeBody(reader, length);
				this.logDebug("Socket 接收数据长度:" + length + ". >> 内容:" + msg);
			}
			else
			{
				msg = this.decodeBody(reader);
				this.logDebug("Socket 接收数据:" + msg);
			}
		}
		catch (Exception e)
		{
			throw e;
		}
		return msg;
	}

	/**
	 * 解码报文体：根据数据长度从Socket的数据接收流中读取字符串并返回(没有对内容进行解析)。
	 * 
	 * @param dataInputStream
	 * @param messageBodylength
	 * @return
	 */
	protected String decodeBody(final DataInputStream dataInputStream, final int messageBodylength)
	{
		byte oneChar = -1;
		StringBuffer sb = new StringBuffer(messageBodylength);
		byte[] bs = new byte[messageBodylength];
		// 报文体解码(非解析XML)
		for (int i = 0; i < messageBodylength; i++)
		{
			byte b;
			try
			{
				b = dataInputStream.readByte();
				bs[i] = b;
			}
			catch (EOFException e)
			{
				this.logDebug("信息长度为：" + messageBodylength + ",但实际长度为：" + i);
			}
			catch (IOException e)
			{
				this.logDebug("报文字节读取异常。");
			}
		}
		sb.append((char) oneChar);
		try
		{
			return new String(bs, Encoding.UTF8);
		}
		catch (UnsupportedEncodingException e)
		{
			this.logDebug("报文字节读取后转换异常。");
			return "-1";
		}
	}

	/**
	 * 解码报文体：直接从Socket的数据接收流中读取字符串并返回(没有对内容进行解析)。
	 * 
	 * @param dataInputStream
	 * @param messageBodylength
	 * @return
	 */
	protected String decodeBody(final DataInputStream input)
	{
		StringBuffer buffer = new StringBuffer();
		int ch;
		try
		{
			while (((ch = input.read()) != -1) || (input.available() != 0))
				buffer = buffer.append((char) ch);
		}
		catch (IOException e)
		{
		}
		return buffer.toString();
	}

	/**
	 * 解码报文头：从接收数据的前4位(即报文头)获取该条数据的长度<br>
	 * ecms项目报文头的定义：32位长度二进制整数，高字节在前，代表报文体长度（字节数，不包含报文头本身长度4）
	 * 
	 * @param dataInputStream
	 * @return
	 */
	protected int decodeHead(final DataInputStream dataInputStream)
	{
		int length = -1;
		byte[] lengthByte = new byte[4];
		for (int i = 0; i < 4; i++)
		{
			try
			{
				lengthByte[i] = dataInputStream.readByte();
			}
			catch (IOException e)
			{
				return -2;
			}
			length = ByteUtils.readInt(lengthByte, 0);
		}
		return length;
	}

	public void close() throws IOException
	{
		isClosed = true;
		socket.close();
	}

	// = 日志接口(ILog)的实现 ============

	@Override
	public void logDebug(final Object message)
	{
		LogHelper.logDebug(this, message);
	}

	@Override
	public void logDebug(final Object message, final Throwable t)
	{
		LogHelper.logDebug(this, message, t);
	}

	@Override
	public void logError(final Object message)
	{
		LogHelper.logError(this, message);
	}

	@Override
	public void logError(final Object message, final Throwable t)
	{
		LogHelper.logError(this, message, t);
	}

	@Override
	public void logInfo(final Object message)
	{
		LogHelper.logInfo(this, message);
	}

	@Override
	public void logInfo(final Object message, final Throwable t)
	{
		LogHelper.logInfo(this, message, t);
	}

	@Override
	public void logWarn(final Object message)
	{
		LogHelper.logWarn(this, message);
	}

	@Override
	public void logWarn(final Object message, final Throwable t)
	{
		LogHelper.logWarn(this, message, t);
	}

}
